package spring.aop.config;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import spring.aop.annotation.Victor;
import spring.aop.dao.IntroductionDao;
import spring.aop.dao.IntroductionDaoImpl;

/**
 * AOP学习: AspectJ 配置
 *      https://docs.spring.io/spring-framework/docs/4.3.12.RELEASE/spring-framework-reference/html/aop.html#aop-ataspectj
 *
 * @author ME
 * @date   2019/11/13
 */
@Component
@Aspect
public class AspectJConfig implements PriorityOrdered {

    @Override
    public int getOrder() {
        // 保证事务等切面先执行
        return Integer.MAX_VALUE;
    }

    /**
     * 方法的一切定义都可以描述: 修饰符、返回类型、包名、方法名、方法参数
      */
    @Pointcut("execution(public * spring.aop.dao.*.*(java.lang.String, ..))")
    public void pointCutExecution() {
    }

    /**
     * 指定类的方法
      */
    @Pointcut("within(spring.aop.dao.*)")
    public void pointCutWithin() {
    }

    /**
     * 目标类方法入参对象是指定类（包含子类）时，切点匹配
      */
    @Pointcut("args(java.lang.Integer, ..)")
    public void pointCutArgs() {
    }

    /**
     * 匹配带有指定注解的方法
     */
    @Pointcut("@annotation(victor)")
    public void pointCutAnnotation(Victor victor) {
    }

    /**
     * 使用 @args:
     * 该函数接收一个注解类的类名，当方法的运行时实际传入的参数类型（包含子类）标注了指定的注解 则匹配
     * 在类继承树中注解点低于入参类型点，则注解点所在类及其子类作为方法入参时，该方法匹配切点 @args(M)
     */
    @Pointcut("@args(spring.aop.annotation.Victor)")
    public void pointCutArgs2() {
    }

    /**
     * this 表示 当前对象(代理对象) 的匹配类型, this 表达式可以轻松的区分是 JDK动态代理还是 CGLIB
     *      springAOP 默认使用 JDK动态代理, 此时代理对象不是 IndexDao的实例
     *      proxyTargetClass = true, 强制使用 CGLIB, 此时代理对象是 IndexDao的实例
     */
    @Pointcut("this(spring.aop.dao.IndexDaoImpl)")
    public void pointCutThis() {
    }

    /**
     * target 表示目标对象的匹配类型
     */
    @Pointcut("target(spring.aop.dao.IndexDaoImpl)")
    public void pointCutTarget() {
    }



    // 匹配带有指定参数类型的方法
    // @Before("pointCutArgs()")

    // 匹配dao包下所有类中不满足 pointCutArgs()表达式，以及满足 pointCutWithin()的 任意方法
    // @Before("pointCutWithin() && !pointCutArgs()")

    // 匹配带有指定注解的方法
    @Before("pointCutExecution()")
    @Order(2)
    public void before222(JoinPoint joinPoint) {
        System.out.println("[Before advice222] getThis(): " + joinPoint.getThis());
    }

    @Before("pointCutExecution()")
    @Order(1)
    public void before111(JoinPoint joinPoint) {
        System.out.println("[Before advice111] getThis(): " + joinPoint.getThis());
    }

    @After("pointCutArgs2() || pointCutExecution()")
    @Order(4)
    public void after() {
        System.out.println("[After advice444] 运行时拦截参数类型上带 @Victor 注解的方法");
    }

    @AfterReturning("pointCutExecution()")
    @Order(3)
    public void afterReturning() {
        System.out.println("[AfterReturning advice333]");
    }

    @AfterThrowing(value = "pointCutExecution()", throwing = "ex")
    public void afterThrowing(Exception ex) {
        System.out.println("[AfterThrowing advice]");
    }

    /** 写法一：目前仅支持 ProceedingJoinPoint 作为第一个参数 */
    // @Around("@annotation(victor)")
    // public Object around(ProceedingJoinPoint pjp, Victor victor) throws Throwable {
    /** 写法二 */
    @Around("pointCutAnnotation(victor)")
    public Object around(ProceedingJoinPoint pjp, Victor victor) throws Throwable {
        System.out.println("[Around advice] 拦截方法上带 @Victor 注解的方法");
        Object[] args = pjp.getArgs();
        System.out.println("[Around advice] before");
        Object retVal = pjp.proceed(args);

        System.out.println("[Around advice] after");
        return retVal;
    }

    /**
     * 给被代理类加入新方法：
     * 1，通过 成员变量的类型 声明需要实现的 接口；
     * 2，使用 value 指定目标类，被代理类。   <p></p>
     * 3，使用注解的 defaultImpl 指定接口的实现类。   <p></p>
     *
     * 给 TestDaoImpl（目标类，被代理类） 引入 IntroductionDao 接口, 其默认实现由 IntroductionDaoImpl.class 提供
     */
    @DeclareParents(value = "spring.aop.dao.TestDaoImpl", defaultImpl = IntroductionDaoImpl.class)
    public static IntroductionDao dao;
}
